package com.shangguosha.game;

import com.shangguosha.core.event.EventBus;
import com.shangguosha.core.event.GameStateEvent;
import com.shangguosha.model.Character;
import com.shangguosha.model.Card;
import com.shangguosha.model.enums.GamePhase;
import com.shangguosha.model.enums.EventType;
import lombok.Data;

import java.util.*;

/**
 * 游戏上下文类 - 管理游戏全局状态
 */
@Data
public class GameContext {
    private static GameContext instance;
    
    private String gameId;
    private List<Character> players;
    private Character currentPlayer;
    private GamePhase currentPhase;
    private int currentPlayerIndex;
    private List<Card> drawPile;
    private List<Card> discardPile;
    private EventBus eventBus;
    private Map<String, Object> gameProperties;
    private boolean gameStarted;
    private boolean gameEnded;
    private Character winner;
    
    private GameContext() {
        this.gameId = UUID.randomUUID().toString();
        this.players = new ArrayList<>();
        this.drawPile = new ArrayList<>();
        this.discardPile = new ArrayList<>();
        this.eventBus = new EventBus();
        this.gameProperties = new HashMap<>();
        this.currentPhase = GamePhase.PREPARE;
        this.currentPlayerIndex = 0;
        this.gameStarted = false;
        this.gameEnded = false;
    }
    
    public static GameContext getInstance() {
        if (instance == null) {
            instance = new GameContext();
        }
        return instance;
    }
    
    /**
     * 重置游戏上下文
     */
    public static void reset() {
        instance = null;
    }
    
    /**
     * 初始化游戏
     */
    public void initializeGame(List<Character> players) {
        if (gameStarted) {
            throw new IllegalStateException("游戏已经开始");
        }
        
        this.players.clear();
        this.players.addAll(players);
        
        // 设置第一个玩家为当前玩家
        if (!players.isEmpty()) {
            this.currentPlayer = players.get(0);
            this.currentPlayerIndex = 0;
        }
        
        GameStateEvent event = new GameStateEvent(EventType.GAME_INITIALIZED.getEventName(), this);
        event.setData(players);
        eventBus.publish(event);
    }
    
    /**
     * 开始游戏
     */
    public void startGame() {
        if (gameStarted) {
            throw new IllegalStateException("游戏已经开始");
        }
        
        if (players.isEmpty()) {
            throw new IllegalStateException("没有玩家");
        }
        
        gameStarted = true;
        currentPhase = GamePhase.PREPARE;
        GameStateEvent event = new GameStateEvent(EventType.GAME_STARTED.getEventName(), this);
        eventBus.publish(event);
    }
    
    /**
     * 结束游戏
     */
    public void endGame(Character winner) {
        if (gameEnded) {
            return;
        }
        
        gameEnded = true;
        this.winner = winner;
        GameStateEvent event = new GameStateEvent(EventType.GAME_ENDED.getEventName(), this);
        event.setData(winner);
        eventBus.publish(event);
    }
    
    /**
     * 切换到下一个玩家
     */
    public void nextPlayer() {
        if (players.isEmpty()) {
            return;
        }
        
        do {
            currentPlayerIndex = (currentPlayerIndex + 1) % players.size();
            currentPlayer = players.get(currentPlayerIndex);
        } while (!currentPlayer.isAlive() && hasAlivePlayers());
        
        GameStateEvent event = new GameStateEvent(EventType.PLAYER_CHANGED.getEventName(), this);
        event.setData(currentPlayer);
        eventBus.publish(event);
    }
    
    /**
     * 切换游戏阶段
     */
    public void nextPhase() {
        GamePhase oldPhase = currentPhase;
        
        switch (currentPhase) {
            case PREPARE:
                currentPhase = GamePhase.JUDGE;
                break;
            case JUDGE:
                currentPhase = GamePhase.DRAW;
                break;
            case DRAW:
                currentPhase = GamePhase.PLAY;
                break;
            case PLAY:
                currentPhase = GamePhase.DISCARD;
                break;
            case DISCARD:
                currentPhase = GamePhase.END;
                break;
            case END:
                nextPlayer();
                currentPhase = GamePhase.PREPARE;
                break;
        }
        
        GameStateEvent event = new GameStateEvent(EventType.PHASE_CHANGED.getEventName(), this, oldPhase, currentPhase);
        eventBus.publish(event);
    }
    
    /**
     * 检查是否有存活的玩家
     */
    public boolean hasAlivePlayers() {
        return players.stream().anyMatch(Character::isAlive);
    }
    
    /**
     * 获取存活的玩家数量
     */
    public int getAlivePlayerCount() {
        return (int) players.stream().filter(Character::isAlive).count();
    }
    
    /**
     * 获取存活的玩家列表
     */
    public List<Character> getAlivePlayers() {
        return players.stream()
                .filter(Character::isAlive)
                .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
    }
    
    /**
     * 根据ID获取玩家
     */
    public Character getPlayerById(String playerId) {
        return players.stream()
                .filter(player -> player.getId().equals(playerId))
                .findFirst()
                .orElse(null);
    }
    
    /**
     * 根据名称获取玩家
     */
    public Character getPlayerByName(String playerName) {
        return players.stream()
                .filter(player -> player.getName().equals(playerName))
                .findFirst()
                .orElse(null);
    }
    
    /**
     * 从牌堆摸牌
     */
    public Card drawCard() {
        if (drawPile.isEmpty()) {
            shuffleDiscardPileToDrawPile();
        }
        
        if (drawPile.isEmpty()) {
            return null;
        }
        
        Card card = drawPile.remove(drawPile.size() - 1);
        GameStateEvent event = new GameStateEvent(EventType.CARD_DRAWN_FROM_PILE.getEventName(), this);
        event.setData(card);
        eventBus.publish(event);
        return card;
    }
    
    /**
     * 将卡牌放入弃牌堆
     */
    public void discardCard(Card card) {
        if (card != null) {
            discardPile.add(card);
            GameStateEvent event = new GameStateEvent(EventType.CARD_DISCARDED_TO_PILE.getEventName(), this);
            event.setData(card);
            eventBus.publish(event);
        }
    }
    
    /**
     * 洗牌 - 将弃牌堆洗入牌堆
     */
    private void shuffleDiscardPileToDrawPile() {
        if (!discardPile.isEmpty()) {
            drawPile.addAll(discardPile);
            discardPile.clear();
            Collections.shuffle(drawPile);
            GameStateEvent event = new GameStateEvent(EventType.DECK_SHUFFLED.getEventName(), this);
            eventBus.publish(event);
        }
    }
    
    /**
     * 设置游戏属性
     */
    public void setProperty(String key, Object value) {
        gameProperties.put(key, value);
    }
    
    /**
     * 获取游戏属性
     */
    public Object getProperty(String key) {
        return gameProperties.get(key);
    }
    
    /**
     * 获取游戏属性（带默认值）
     */
    @SuppressWarnings("unchecked")
    public <T> T getProperty(String key, T defaultValue) {
        Object value = gameProperties.get(key);
        return value != null ? (T) value : defaultValue;
    }
    
    // Getters and Setters
    public String getGameId() {
        return gameId;
    }
    
    public List<Character> getPlayers() {
        return new ArrayList<>(players);
    }
    
    public Character getCurrentPlayer() {
        return currentPlayer;
    }
    
    public void setCurrentPlayer(Character currentPlayer) {
        this.currentPlayer = currentPlayer;
        this.currentPlayerIndex = players.indexOf(currentPlayer);
    }
    
    public GamePhase getCurrentPhase() {
        return currentPhase;
    }

    public void setDrawPile(List<Card> drawPile) {
        this.drawPile = new ArrayList<>(drawPile);
    }
    

    public EventBus getEventBus() {
        return eventBus;
    }
    
    public boolean isGameStarted() {
        return gameStarted;
    }
    
    public boolean isGameEnded() {
        return gameEnded;
    }
    
    public Character getWinner() {
        return winner;
    }

}